home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-10-11 | 55.0 KB | 2,048 lines | [TEXT/MMCC] |
- /*----------------------------------------------------------------------------
-
- net.c
-
- This reusable module handles low-level communications with TCP/IP
- network servers, using a simple "net ASCII" command/response
- stream model. It also exports several functions for using FTP data streams,
- and several functions for doing domain name resolver tasks. The module
- hides the complexity and peculiarities of the MacTCP device driver.
-
- The module also manages many of the common protocol details of communicating
- with net ASCII servers, including all of the following:
-
- Handling initial server "hello" messages.
- Handling final server "QUIT" commands.
- Mapping between CR line terminators and CRLF line terminators.
- Decoding numeric server response codes.
- Skipping server comments (response lines with "-" following the response code).
- Supplying terminating "." lines when sending blocks of text.
- Recognizing and discarding terminating "." lines when reading blocks of text.
- Mapping between leading "." characters and leading ".." characters on text lines.
-
- The module emphasizes simplicity at the expense of power. There are many things
- you can do by calling MacTCP directly which you cannot do with this module.
- The module is *not* a general purpose interface to MacTCP designed to meet the
- needs of all possible Mac TCP/IP networking programs. It is only a convenient
- interface for writing clients for typical simple net ASCII servers.
-
- The following mandatory functions handle initialization, idle time, and
- termination tasks.
-
- NetInit - Initialize the module.
- NetIdle - Handle idle time tasks.
- NetTerm - Terminate the module.
-
- The following functions work with Net ASCII command/response streams.
-
- NetOpen - Open a stream.
- NetClose - Close a stream.
- NetCommand - Send a command and get the response.
- NetGetExtraResponse - Get an extra command response.
- NetBatchedCommands - Send several commands in a batch and
- get and process the responses.
- NetSendText - Send command text.
- NetGetText - Get response text.
- NetGetTextWithTruncation - Get response text with possible truncation.
- NetGetChunkyText - Get response text in chunks.
-
- The following functions work with FTP data streams. They are used by
- the ftp.c module.
-
- NetFTPDataOpen - Open an FTP data stream.
- NetFTPDataClose - Close an FTP data stream.
- NetFTPDataWaitForConnection - Wait for the FTP server to open
- its end of the FTP data stream.
- NetGetFTPTextFile - Get a text file.
- NetSendFTPTextFile - Send a text file.
-
- The following functions handle simple domain name resolver and related tasks.
-
- NetGetMyAddr - Get my IP address.
- NetGetMyAddrStr - Get my IP address as a dotted-decimal string.
- NetGetMyName - Get my domain name.
- NetNameToAddr - Convert a domain name to an IP address.
- NetAddrToName - Convert an IP address to a domain name.
-
- The following function can be used by client modules to give time to background
- processes and check for user cancel operations.
-
- NetGiveTime - Give time to other processes.
-
- The following function can be used by the GiveTime function to determine whether
- or not a DNR operation is in progress:
-
- NetDNROperationInProgress - Determine if a DNR operation is in progress.
-
- A "stream" is an abstraction representing a bidirectional network connection
- to a TCP/IP server. The notion of a "stream" in this module combines the
- separate MacTCP notions of "stream" and "connection". When calling MacTCP
- directly, you "create" and "release" streams and "open" and "close" connections.
- These operations are combined in the net.c module. The "NetOpen" function both
- creates a stream and opens a connection. The "NetClose" function both
- closes the connection and releases the stream.
-
- A stream is represented as a handle of type "NetStreamHandle". These stream
- handles are opaque. You may copy them and pass them as parameters to
- functions in net.c, but you are prohibited from accessing the contents of
- the memory blocks pointed to by the stream handles. Only the functions in
- net.c are permitted to manipulate the contents of these blocks.
-
- All of the functions exported by this module return an OSErr error code
- as the function result. This error code may have any of the following
- values:
-
- noErr if no error occurred.
- userCanceledErr if the user canceled the operation.
- other any other OS, Toolbox, or MacTCP error code.
-
- In all of the functions which perform operations on streams, if the
- user cancels the operation or some other error occurs, the stream
- is aborted before returning to the caller. "Aborted" means that the
- server connection is closed abruptly, without going through the usual TCP
- stream teardown process. The stream is also deallocated. In this case, you
- must not attempt to reuse the stream handle, since the stream no
- longer exists. You must perform careful error checking.
-
- Copyright © 1994, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
-
- #include "MacTCPCommonTypes.h"
- #include "TCPPB.h"
- #include "AddressXlation.h"
- #include "GetMyIPAddr.h"
-
- #include "def.h"
- #include "net.h"
- #include "memutil.h"
- #include "strutil.h"
-
-
-
- /* Constants. */
-
- #define kMacTCPBufSize 16000 /* MacTCP stream buffer size */
- #define kInputBufSize 4000 /* input buffer size */
- #define kInputBufSizeDiv2 (kInputBufSize/2)
-
-
-
- /* Types. */
-
- typedef struct TStream {
- TCPiopb pBlock; /* MacTCP parameter block (must be first) */
- unsigned long ipAddr; /* IP address of host */
- StreamPtr tcpStream; /* MacTCP stream pointer */
- struct wdsEntry wds[3]; /* MacTCP write data structure */
- char *in; /* pointer to next location in input buffer in
- which incoming network bytes should be stored */
- char *out; /* pointer to next location in input buffer from
- which bytes should be removed and delivered
- to our clients (note: we always have out <= in) */
- long myA5; /* our A5 register */
- Boolean finished; /* true when asynch close finished */
- struct TStream **next; /* handle to next asynch stream close queue element */
- char mactcpBuf[kMacTCPBufSize]; /* MacTCP buffer */
- char inputBuf[kInputBufSize]; /* input buffer */
- Boolean fromFreeList; /* true if storage obtained from free list */
- struct TStream **nextFree; /* handle to next available free buffer */
- } TStream, *TStreamPtr, **TStreamHandle;
-
-
-
- /* Global variables. */
-
- static TStreamHandle gFreeStreamStorage = nil; /* handle to first free buffer */
- static NetGiveTimeFunction gGiveTime; /* pointer to GiveTime function */
- static NetLogFunction gLog; /* pointer to logging function */
- static short gRefNum; /* MacTCP driver reference number */
- static TStreamHandle gCloseQueue = nil; /* handle to queue of asynch closing streams */
-
- static ResultUPP gDNRResultProcUPP = nil;
- static TCPIOCompletionUPP gCloseStreamCompletionRoutineUPP = nil;
-
- static Boolean gDNROperationInProgress = false;
-
-
-
- /*----------------------------------------------------------------------------
- NewStreamBuffer
-
- Allocate a stream buffer.
-
- Exit: function result = error code.
- *sh = handle to new stream buffer.
- ----------------------------------------------------------------------------*/
-
- static OSErr NewStreamBuffer (TStreamHandle *sh)
- {
- OSErr err = noErr;
-
- if (gFreeStreamStorage == nil) {
- err = MyNewHandle(sizeof(TStream), sh);
- if (err != noErr) return err;
- MyHLockHi(*sh);
- (***sh).fromFreeList = false;
- } else {
- *sh = gFreeStreamStorage;
- gFreeStreamStorage = (**gFreeStreamStorage).nextFree;
- memset(**sh, 0, sizeof(TStream));
- (***sh).fromFreeList = true;
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DisposeStreamBuffer
-
- Dispose a stream buffer.
-
- Entry: sh = handle to stream buffer.
- ----------------------------------------------------------------------------*/
-
- static void DisposeStreamBuffer (TStreamHandle sh)
- {
- if ((**sh).fromFreeList) {
- (**sh).nextFree = gFreeStreamStorage;
- gFreeStreamStorage = sh;
- } else {
- MyDisposeHandle(sh);
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- InitTCPBlock
-
- Initialize a MacTCP parameter block.
-
- Entry: pBlock = pointer to parameter block.
- csCode = MacTCP control code (TCPCreate, etc.).
- ----------------------------------------------------------------------------*/
-
- static void InitTCPBlock (TCPiopb *pBlock, short csCode)
- {
- memset(pBlock, 0, sizeof(TCPiopb));
- pBlock->ioResult = 1;
- pBlock->ioCRefNum = gRefNum;
- pBlock->csCode = csCode;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CallMacTCP
-
- Call MacTCP.
-
- Entry: s = pointer to stream.
- returnImmediatelyOnCancel = true to return immediately after
- a user cancel operation.
-
- Exit: function result = system error result code.
- ----------------------------------------------------------------------------*/
-
- static OSErr CallMacTCP (TStreamPtr s, Boolean returnImmediatelyOnCancel)
- {
- OSErr err = noErr;
-
- PBControlAsync((ParmBlkPtr)&s->pBlock);
- do {
- err = (*gGiveTime)(true);
- if (err != noErr && returnImmediatelyOnCancel) return err;
- } while (s->pBlock.ioResult > 0);
- if (err == noErr) err = s->pBlock.ioResult;
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPCreate
-
- Create a MacTCP stream.
-
- Entry: sh = handle to stream.
-
- Exit: function result = system error result code.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPCreate (TStreamHandle sh)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- s = (TStreamPtr)StripAddress(*sh);
- InitTCPBlock(&s->pBlock, TCPCreate);
- s->pBlock.csParam.create.rcvBuff = s->mactcpBuf;
- s->pBlock.csParam.create.rcvBuffLen = kMacTCPBufSize;
- err = CallMacTCP(s, false);
- s->tcpStream = s->pBlock.tcpStream;
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPRelease
-
- Release a MacTCP stream.
-
- Entry: sh = handle to stream.
-
- Exit: function result = system error result code.
-
- Note: Any active connection is also aborted, if necessary, before
- releasing the stream.
-
- Note: This function uses its own MacTCP parameter block instead of the
- one inside the stream block, because the one inside the stream block
- may already be in use by some other asynchronous operation.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPRelease (TStreamHandle sh)
- {
- TStreamPtr s;
- TCPiopb pBlock;
- OSErr err = noErr;
-
- s = (TStreamPtr)StripAddress(*sh);
- InitTCPBlock(&pBlock, TCPRelease);
- pBlock.tcpStream = s->tcpStream;
- PBControlAsync((ParmBlkPtr)&pBlock);
- while (pBlock.ioResult > 0) err = (*gGiveTime)(true);
- if (err == noErr) err = pBlock.ioResult;
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPActiveOpen
-
- Open an active MacTCP connection to a server.
-
- Entry: sh = handle to stream.
- addr = IP address of server.
- port = port number of service.
-
- Exit: function result = system error result code.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPActiveOpen (TStreamHandle sh, unsigned long addr,
- unsigned short port)
- {
- TStreamPtr s;
-
- s = (TStreamPtr)StripAddress(*sh);
- InitTCPBlock(&s->pBlock, TCPActiveOpen);
- s->pBlock.tcpStream = s->tcpStream;
- s->pBlock.csParam.open.remoteHost = addr;
- s->pBlock.csParam.open.remotePort = port;
- return CallMacTCP(s, true);
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPPassiveOpen
-
- Open a passive MacTCP connection.
-
- Entry: sh = handle to stream.
-
- Exit: function result = system error result code.
- *port = assigned unused local port number.
-
- Note: Unlike the other "DoTCPxxx" functions, DoTCPPassiveOpen is
- asynchronous. The passive stream is opened, but the function does not
- wait for another host to connect. The caller is responsible for doing
- this by polling s->pBlock.ioResult and waiting for it to become <= 0.
- The function does, however, wait for the local port number to be assigned,
- and it returns this local port number to the caller in the "port" parameter.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPPassiveOpen (TStreamHandle sh, unsigned short *port)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- s = (TStreamPtr)StripAddress(*sh);
- InitTCPBlock(&s->pBlock, TCPPassiveOpen);
- s->pBlock.tcpStream = s->tcpStream;
- PBControlAsync((ParmBlkPtr)&s->pBlock);
- while (s->pBlock.csParam.open.localPort == 0) {
- err = (*gGiveTime)(true);
- if (err != noErr) return err;
- }
- *port = s->pBlock.csParam.open.localPort;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPSend
-
- Send data on a MacTCP stream.
-
- Entry: sh = handle to stream.
- (**sh).wds = MacTCP write data structure initialized to send
- the data.
-
- Exit: function result = system error result code.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPSend (TStreamHandle sh)
- {
- TStreamPtr s;
-
- s = (TStreamPtr)StripAddress(*sh);
- s->in = s->out = s->inputBuf;
- InitTCPBlock(&s->pBlock, TCPSend);
- s->pBlock.tcpStream = s->tcpStream;
- s->pBlock.csParam.send.wdsPtr = (Ptr)s->wds;
- return CallMacTCP(s, true);
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPRcv
-
- Reveive data on a MacTCP stream.
-
- Entry: sh = handle to stream.
- data = pointer to data buffer.
- *len = length of data buffer.
-
- Exit: function result = system error result code.
- *len = number of bytes received.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPRcv (TStreamHandle sh, Ptr data, unsigned short *len)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- s = (TStreamPtr)StripAddress(*sh);
- InitTCPBlock(&s->pBlock, TCPRcv);
- s->pBlock.tcpStream = s->tcpStream;
- s->pBlock.csParam.receive.rcvBuff = StripAddress(data);
- s->pBlock.csParam.receive.rcvBuffLen = *len;
- err = CallMacTCP(s, true);
- *len = s->pBlock.csParam.receive.rcvBuffLen;
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SendCommand
-
- Send a command on a stream.
-
- Entry: sh = handle to stream.
- command = C-format command string.
-
- Exit: function result = system error result code.
- ----------------------------------------------------------------------------*/
-
- static OSErr SendCommand (TStreamHandle sh, char *command)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- s = (TStreamPtr)StripAddress(*sh);
- s->wds[0].length = strlen(command);
- s->wds[0].ptr = StripAddress(command);
- s->wds[1].length = 2;
- s->wds[1].ptr = CRLF;
- s->wds[2].length = 0;
- s->wds[2].ptr = nil;
- err = DoTCPSend(sh);
- if (err != noErr) return err;
- if (gLog != nil) (*gLog)(true, s->ipAddr, command);
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- ReadResponse
-
- Read a command response from a stream.
-
- Entry: sh = handle to stream.
-
- Exit: function result = system error result code.
- *responseCode = server response code.
- response = C-format server response string, including the
- response code.
-
- Response comment lines are discarded (response lines with a
- '-' immediately following the response code).
- ----------------------------------------------------------------------------*/
-
- static OSErr ReadResponse (TStreamHandle sh, long *responseCode, CStr255 response)
- {
- TStreamPtr s;
- char *in, *out, *p, *q, *r;
- unsigned short len, numUnread;
- long num;
- OSErr err = noErr;
-
- s = (TStreamPtr)StripAddress(*sh);
- in = s->in;
- out = s->out;
-
- while (true) {
- while (out < in) {
- for (p = out; p < in-1 && !(*p == CR && *(p+1) == LF); p++) /* do nothing */ ;
- if (p >= in-1) break;
- *p = 0;
- q = out;
- out = p+2;
- r = q;
- num = CrackNum(&r);
- if (*r != '-' && num != 0) {
- if (p - q > 255) *(q+255) = 0;
- strcpy(response, q);
- *responseCode = num;
- s->in = in;
- s->out = out;
- if (gLog != nil) (*gLog)(false, s->ipAddr, response);
- return noErr;
- }
- }
- if (out == in) {
- out = in = s->inputBuf;
- } else if (in > s->inputBuf + kInputBufSizeDiv2) {
- numUnread = in - out;
- if (numUnread > kInputBufSizeDiv2) {
- numUnread = kInputBufSizeDiv2;
- out = in - numUnread;
- }
- BlockMoveData(out, s->inputBuf, numUnread);
- out = s->inputBuf;
- in = out + numUnread;
- }
- len = s->inputBuf + kInputBufSize - in;
- err = DoTCPRcv(sh, in, &len);
- if (err != noErr) return err;
- in += len;
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- DNRResultProc
-
- MacTCP domain name resolver completion routine.
-
- Entry: hInfoPtr = pointer to hostInfo struct.
- userDataPtr = pointer to user data.
- ----------------------------------------------------------------------------*/
-
- static pascal void DNRResultProc (struct hostInfo *hInfoPtr, char *userDataPtr)
- {
- *(Boolean*)userDataPtr = true;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CloseStreamCompletionRoutine
-
- This is the asynchronous completion routine used to close streams.
-
- This completion routine chains to itself to do the following tasks
- involved in gracefully tearing down a stream in the background:
-
- Wait for QUIT command send to complete (the send is initiated by the
- NetClose function below).
- Read incoming data until an error occurs (signalling that the
- server has closed its end of the connection).
- Close our end of the connection.
-
- Entry: pBlock = pointer to MacTCP parameter block.
- ----------------------------------------------------------------------------*/
-
- static void CloseStreamCompletionRoutine (TCPiopb *pBlock)
- {
- TStreamPtr s;
- OSErr err = noErr;
- long savedA5;
-
- s = (TStreamPtr)pBlock;
-
- savedA5 = SetA5(s->myA5);
-
- if (s->pBlock.csCode == TCPSend) {
-
- /* The QUIT command has been sent. Start the first receive. */
-
- if (s->pBlock.ioResult != noErr) {
- s->finished = true;
- SetA5(savedA5);
- return;
- }
- InitTCPBlock(&s->pBlock, TCPRcv);
- s->pBlock.ioCompletion =
- (TCPIOCompletionProc)gCloseStreamCompletionRoutineUPP;
- s->pBlock.tcpStream = s->tcpStream;
- s->pBlock.csParam.receive.rcvBuff = s->inputBuf;
- s->pBlock.csParam.receive.rcvBuffLen = kInputBufSize;
-
- } else if (s->pBlock.csCode == TCPRcv) {
-
- /* A receive operation has finished. If there was no error, start another
- receive. If an error occurred, it was because the server has closed
- its side of the connection. In this case, we start the TCPClose. */
-
- err = s->pBlock.ioResult;
- InitTCPBlock(&s->pBlock, err == noErr ? TCPRcv : TCPClose);
- s->pBlock.ioCompletion =
- (TCPIOCompletionProc)gCloseStreamCompletionRoutineUPP;
- s->pBlock.tcpStream = s->tcpStream;
- if (err == noErr) {
- s->pBlock.csParam.receive.rcvBuff = s->inputBuf;
- s->pBlock.csParam.receive.rcvBuffLen = kInputBufSize;
- }
-
- } else if (s->pBlock.csCode == TCPClose) {
-
- /* The close has finished. Set the "finished" field to signal
- that we can now release the stream and dispose of the queue
- element. */
-
- s->finished = true;
- SetA5(savedA5);
- return;
-
- }
-
- /* Issue the next PBControl call in the chain. */
-
- s->pBlock.ioResult = 1;
- err = PBControlAsync((ParmBlkPtr)&s->pBlock);
- if (err != noErr) s->finished = true;
- SetA5(savedA5);
-
- }
-
-
-
- /*----------------------------------------------------------------------------
- MungeOut
-
- Munge a block of text before sending it out to a server.
-
- Entry: text = handle to CR-terminated ASCII text lines.
- Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
- mungePeriods = true to munge periods.
-
- Exit: function result = system error result code.
-
- CR line terminators are translated to CRLF. If the text block does
- not have a terminating CR at the end of the last line, one is added.
-
- If mungePeriods=true, leading "." characters on lines are mapped to
- "..", and a terminating "." on a line by itself is added to the end
- of the text block.
- ----------------------------------------------------------------------------*/
-
- static OSErr MungeOut (Handle text, Boolean mungePeriods)
- {
- OSErr err = noErr;
- char *p, *pEnd, *pStart, *q;
- long textLen, oldTextLen;
-
- textLen = MyGetHandleSize(text);
- p = *text;
- pEnd = p + textLen;
- if (textLen == 0 || *(pEnd-1) != CR) {
- textLen++;
- err = MySetHandleSize(text, textLen);
- if (err != noErr) return err;
- p = *text;
- pEnd = p + textLen;
- *(pEnd-1) = CR;
- }
- oldTextLen = textLen;
- if (mungePeriods) {
- while (p < pEnd) {
- if (*p == '.') textLen++;
- while (*p != CR) p++;
- p++;
- textLen++;
- }
- textLen += 3;
- } else {
- for (; p < pEnd; p++) if (*p == CR) textLen++;
- }
- err = MySetHandleSize(text, textLen);
- if (err != noErr) return err;
- pStart = *text;
- p = pStart + oldTextLen - 1;
- q = pStart + textLen - 1;
- if (mungePeriods) {
- *q-- = LF;
- *q-- = CR;
- *q-- = '.';
- while (p >= pStart) {
- *q-- = LF;
- *q-- = CR;
- p--;
- while (*p != CR && p >= pStart) *q-- = *p--;
- if (*(p+1) == '.') *q-- = '.';
- }
- } else {
- while (p >= pStart) {
- if (*p == CR) *q-- = LF;
- *q-- = *p--;
- }
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MungeIn
-
- Munge a block of text after receiving it from a server.
-
- Entry: text = handle to ASCII text lines as received from server.
- Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
- textLen = number of characters in text block.
- mungePeriods = true to munge periods.
-
- Exit: function result = system error result code.
-
- CRLF line terminators are translated to CR. A final CR is added at the
- end of the last line if necessary.
-
- If mungePeriods=true, leading ".." characters on lines are mapped to
- ".", and the terminating "." on a line by itself is removed from the end
- of the text block.
- ----------------------------------------------------------------------------*/
-
- static OSErr MungeIn (Handle text, long textLen, Boolean mungePeriods)
- {
- OSErr err = noErr;
- char *pEnd, *p, *q;
- Boolean needTerminatingCR = false;
-
- if (mungePeriods && textLen >= 3) {
- p = *text + textLen - 3;
- if (*p == '.' && *(p+1) == CR && *(p+2) == LF) textLen -= 3;
- }
- p = q = *text;
- pEnd = p + textLen;
- if (mungePeriods) {
- while (p < pEnd) {
- if (*p == '.' && *(p+1) == '.') {
- *q++ = '.';
- p += 2;
- }
- while (*p != CR || *(p+1) != LF) *q++ = *p++;
- *q++ = CR;
- p += 2;
- }
- } else {
- while (p < pEnd) {
- if (*p == CR && *(p+1) == LF) {
- *q++ = CR;
- p += 2;
- } else {
- *q++ = *p++;
- }
- }
- }
- textLen = q - *text;
- if (textLen > 0 && *(q-1) != CR) {
- textLen++;
- needTerminatingCR = true;
- }
- err = MySetHandleSize(text, textLen);
- if (err != noErr) return err;
- if (needTerminatingCR) *(*text + textLen - 1) = CR;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetText
-
- Get response text from a server.
-
- Entry: stream = handle to stream.
- truncateFunction = pointer to truncate function, or nil if none.
-
- Exit: function result = system error result code.
- *text = handle to received ASCII text lines. Each line is
- terminated with a CR.
- *truncated = true if text truncated.
-
- CRLF line terminators are translated to CR. The server must send a
- terminating "." on a line by itself. This terminating line is not
- included in the "text" handle returned to the caller. Leading
- ".." characters on lines are mapped to ".".
-
- The truncation function is called every time a new block of text is
- received from the server. This function must be declared as follows:
-
- Boolean TruncateFunction (Handle t, long tLen, long *pos)
-
- Entry: t = handle to raw text received from server so far.
- tLen = length of text received from server so far.
- *pos = saved position in text (0 on first call).
-
- Exit: function result = true if text should be truncated.
- *pos = updated saved position in text if no truncation,
- length of truncated text if truncated.
-
- If the truncate function returns true, GetText aborts
- the stream and returns to the caller immediately.
-
- The text passed to the truncate function is raw. It contains CRLF line
- terminators and doubled leading ".." characters. The text returned to
- NetGetTextWithTruncation's caller, however, is processed as indicated
- above.
- ----------------------------------------------------------------------------*/
-
- static OSErr GetText (NetStreamHandle stream, NetTruncateFunction truncateFunction,
- Handle *text, Boolean *truncated)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
- Handle t = nil;
- long tLen = 0;
- long tAllocated = 0;
- char *tEnd;
- long numFree;
- unsigned short len;
- long pos = 0;
- Boolean trunc = false;
-
- sh = (TStreamHandle)stream;
- s = (TStreamPtr)StripAddress(*sh);
- tLen = s->in - s->out;
- tAllocated = tLen + 4000;
- err = MyNewHandle(tAllocated, &t);
- if (err != noErr) goto exit;
- if (tLen > 0) BlockMoveData(s->out, *t, tLen);
- s->in = s->out = s->inputBuf;
-
- while (true) {
- if (truncateFunction != nil && (*truncateFunction)(t, tLen, &pos)) {
- trunc = true;
- tLen = pos;
- break;
- }
- if (tLen >= 3) {
- tEnd = *t + tLen;
- if (*(tEnd-3) == '.' && *(tEnd-2) == CR && *(tEnd-1) == LF) {
- if (tLen == 3) break;
- if (tLen >= 5 && *(tEnd-5) == CR && *(tEnd-4) == LF) break;
- }
- }
- numFree = tAllocated - tLen;
- if (numFree <= 4000) {
- tAllocated += 4000;
- err = MySetHandleSize(t, tAllocated);
- if (err != noErr) goto exit;
- }
- len = 4000;
- MyHLock(t);
- err = DoTCPRcv(sh, *t + tLen, &len);
- if (err != noErr) goto exit;
- MyHUnlock(t);
- tLen += len;
- }
-
- err = MungeIn(t, tLen, true);
- if (err != noErr) goto exit;
-
- *text = t;
- if (trunc) {
- DoTCPRelease(sh);
- DisposeStreamBuffer(sh);
- }
- *truncated = trunc;
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- DisposeStreamBuffer(sh);
- MyDisposeHandle(t);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetInit
-
- Initialize the net.c module. This function must be called before calling
- any other functions in the module. It opens the MacTCP driver.
-
- Entry: giveTime = pointer to give time function.
- log = pointer to logging function, or nil if none.
- numBuffs = number of stream buffers to preallocate and lock
- at the top of the heap.
-
- Exit: function result = system error result code.
-
- The give time function is called repeatedly during all network
- operations. It must be declared as follows:
-
- OSErr GiveTime (Boolean waiting)
-
- This function should do two things: give time to other processes by calling
- WaitNextEvent, and check for user cancel events (e.g., clicking on a Cancel
- button or pressing Command-period or Escape). The function must return
- userCanceledErr if the user has canceled the operation, noErr otherwise. If
- waiting=true, the function should call WaitNextEvent to give time to other
- processes continuously. If waiting=false, the function may throttle
- WaitNextEvent calls to every 4-6 ticks. The net.c module calls the function
- with waiting=true.
-
- The logging function is called once for each server command and response.
- It must be declared as follows:
-
- void Log (Boolean command, unsigned long ipAddr, char *str)
- ----------------------------------------------------------------------------*/
-
- OSErr NetInit (NetGiveTimeFunction giveTime, NetLogFunction log, short numBuffs)
- {
- short i;
- TStreamHandle sh;
- OSErr err = noErr;
-
- gGiveTime = giveTime;
- gLog = log;
- for (i = 0; i < numBuffs; i++) {
- err = MyNewHandle(sizeof(TStream), &sh);
- if (err != noErr) return err;
- MyHLockHi(sh);
- (**sh).nextFree = gFreeStreamStorage;
- gFreeStreamStorage = sh;
- }
- return OpenDriver("\p.IPP", &gRefNum);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetIdle
-
- Handle idle time tasks. You should call this function in your main
- event loop.
-
- Exit: function result = system error result code (always noErr).
-
- This function takes care of releasing and deallocating streams which
- have finished closing asynchronously in the background.
- ----------------------------------------------------------------------------*/
-
- OSErr NetIdle (void)
- {
- TStreamHandle sh, next, prev = nil;
- TStreamPtr s;
-
- for (sh = gCloseQueue; sh != nil; sh = next) {
- s = (TStreamPtr)StripAddress(*sh);
- next = s->next;
- if (s->finished) {
- DoTCPRelease(sh);
- if (prev == nil) {
- gCloseQueue = next;
- } else {
- (**prev).next = next;
- }
- DisposeStreamBuffer(sh);
- } else {
- prev = sh;
- }
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetTerm
-
- Terminate the module. You must call this function when you quit.
-
- Exit: function result = system error result code (always noErr).
-
- This functions waits until all asynchronous stream close operations have
- completed, or for 5 seconds, whichever happens first. If any close
- operations are still pending after 5 seconds, they are aborted.
- ----------------------------------------------------------------------------*/
-
- OSErr NetTerm (void)
- {
- TStreamHandle sh;
- long waitTil;
-
- waitTil = TickCount() + 300;
- while (gCloseQueue != nil && TickCount() < waitTil) {
- (*gGiveTime)(true);
- NetIdle();
- }
- if (gCloseQueue != nil) {
- for (sh = gCloseQueue; sh != nil; sh = (**sh).next) (**sh).finished = true;
- NetIdle();
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetOpen
-
- Open a stream.
-
- Entry: addr = IP address of server.
- port = port number of service.
-
- Exit: function result = system error result code.
- *stream = handle to opened stream.
- *responseCode = server hello message response code.
- response = C-format hello message string, including the
- response code.
-
- Hello message comment lines are discarded (response lines with a
- '-' immediately following the response code).
- ----------------------------------------------------------------------------*/
-
- OSErr NetOpen (unsigned long addr, unsigned short port, NetStreamHandle *stream,
- long *responseCode, CStr255 response)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
-
- err = NewStreamBuffer(&sh);
- if (err != noErr) return err;
- s = (TStreamPtr)StripAddress(*sh);
- s->in = s->out = s->inputBuf;
- s->ipAddr = addr;
- s->myA5 = SetCurrentA5();
- err = DoTCPCreate(sh);
- if (err != noErr) goto exit;
- err = DoTCPActiveOpen(sh, addr, port);
- if (err != noErr) goto exit;
- err = ReadResponse(sh, responseCode, response);
- if (err != noErr) goto exit;
- *stream = (NetStreamHandle)sh;
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- DisposeStreamBuffer(sh);
- if (err == connectionClosing || err == connectionDoesntExist ||
- err == connectionTerminated) err = openFailed;
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetClose
-
- Close a stream.
-
- Entry: stream = handle to stream.
-
- Exit: function result = system error result code (always noErr).
-
- A QUIT command is sent to the server before closing the stream.
- Thus you do not need to and should not send this command yourself.
-
- To improve performance, all streams are closed asynchronously using
- chained MacTCP device driver completion routines. The function returns
- immediately without any delay.
-
- This asynchronous stream closing feature also permits you to close
- connections in the background without interfering with or delaying user
- actions in the foreground.
- ----------------------------------------------------------------------------*/
-
- OSErr NetClose (NetStreamHandle stream)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
-
- sh = (TStreamHandle)stream;
- s = (TStreamPtr)StripAddress(*sh);
-
- /* Link the stream into the queue of closing streams. */
-
- s->myA5 = SetCurrentA5();
- s->finished = false;
- s->next = gCloseQueue;
- gCloseQueue = sh;
-
- /* Send an asynchronous "QUIT" command to the server, chained
- to our completion routine . */
-
- s->wds[0].length = 6;
- s->wds[0].ptr = "QUIT\r\n";
- s->wds[1].length = 0;
- s->wds[1].ptr = nil;
- InitTCPBlock(&s->pBlock, TCPSend);
- s->pBlock.csParam.send.wdsPtr = (Ptr)s->wds;
- s->pBlock.ioCompletion =
- (TCPIOCompletionProc)gCloseStreamCompletionRoutineUPP;
- s->pBlock.tcpStream = s->tcpStream;
- err = PBControlAsync((ParmBlkPtr)&s->pBlock);
- if (err != noErr) s->finished = true;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetCommand
-
- Send a command to a server and get the response.
-
- Entry: stream = handle to stream.
- command = C-format command string.
-
- Exit: function result = system error result code.
- *responseCode = server response code.
- response = C-format server response string, including the
- response code.
-
- Response comment lines are discarded (response lines with a
- '-' immediately following the response code).
- ----------------------------------------------------------------------------*/
-
- OSErr NetCommand (NetStreamHandle stream, char *command,
- long *responseCode, CStr255 response)
- {
- TStreamHandle sh;
- OSErr err = noErr;
-
- sh = (TStreamHandle)stream;
- err = SendCommand(sh, command);
- if (err != noErr) goto exit;
- err = ReadResponse(sh, responseCode, response);
- if (err != noErr) goto exit;
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- DisposeStreamBuffer(sh);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetExtraResponse
-
- Get an extra command response from a server.
-
- Entry: stream = handle to stream.
-
- Exit: function result = system error result code.
- *responseCode = server response code.
- response = C-format server response string, including the
- response code.
-
- Response comment lines are discarded (response lines with a
- '-' immediately following the response code).
-
- Some servers have commands which return more than one response. The
- NetCommand function above reads the first response. This function is
- used to read the additional response(s). The FTP RETR and STOR commands
- are examples.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetExtraResponse (NetStreamHandle stream, long *responseCode,
- CStr255 response)
- {
- TStreamHandle sh;
- OSErr err = noErr;
-
- sh = (TStreamHandle)stream;
- err = ReadResponse(sh, responseCode, response);
- if (err != noErr) goto exit;
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- DisposeStreamBuffer(sh);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetBatchedCommands
-
- Send multiple batched commands to a server and get and process
- the responses.
-
- Entry: stream = handle to stream.
- commands = handle to CR-terminated commands.
- Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
- doOneResponse = pointer to response processing function.
-
- Exit: function result = system error result code.
-
- The response processing function must be declared as follows:
-
- void DoOneResponse (long responseCode, CStr255 response)
-
- The function is called once for each command response. It is passed the
- following parameters:
-
- responseCode = server response code.
- response = C-format server response string, including the
- response code.
-
- Response comment lines are discarded (leading response lines with a
- '-' immediately following the response code).
-
- CR line terminators are mapped to CRLF line terminators.
-
- When you know in advance that you need to send a number of commands to a
- server all in a row, it is usually much more efficient to send them in
- a batch rather than sending them one at a time using the NetCommand function
- above. You should take care, however, since not all servers support batched
- commands.
-
- All of the batched commands must return only a single response (after
- skipping any possible comment lines).
- ----------------------------------------------------------------------------*/
-
- OSErr NetBatchedCommands (NetStreamHandle stream, Handle commands,
- NetDoOneResponse doOneResponse)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
- char *p, *q, *r;
- long cmdLen;
- unsigned short len;
- long responseCode;
- CStr255 response;
- short numCmds, i;
- char state;
-
- state = MyHGetState(commands);
-
- err = MungeOut(commands, false);
- if (err != noErr) goto exit;
- cmdLen = MyGetHandleSize(commands);
-
- sh = (TStreamHandle)stream;
- s = (TStreamPtr)StripAddress(*sh);
- s->wds[1].length = 0;
- s->wds[1].ptr = nil;
- MyHLock(commands);
- p = *commands;
- while (cmdLen > 0) {
- len = cmdLen > 4000 ? 4000 : cmdLen;
- q = p + len - 1;
- while (q > p && (*q != LF || *(q-1) != CR)) q--;
- if (q == p) {
- p += len;
- cmdLen -= len;
- continue;
- } else {
- numCmds = 0;
- len = q - p + 1;
- while (q > p) {
- if (*q == LF && *(q-1) == CR) {
- numCmds++;
- q -= 2;
- } else {
- q--;
- }
- }
- }
- s->wds[0].length = len;
- s->wds[0].ptr = StripAddress(p);
- err = DoTCPSend(sh);
- if (err != noErr) goto exit;
- if (gLog != nil) {
- q = p;
- for (i = 0; i < numCmds; i++) {
- r = q;
- while (*r != CR || *(r+1) != LF) r++;
- *r = 0;
- (*gLog)(true, s->ipAddr, q);
- q = r + 2;
- }
- }
- p += len;
- cmdLen -= len;
- while (numCmds--) {
- err = ReadResponse(sh, &responseCode, response);
- if (err != noErr) goto exit;
- (*doOneResponse)(responseCode, response);
- }
- }
- MyHSetState(commands, state);
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- DisposeStreamBuffer(sh);
- MyHSetState(commands, state);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetSendText
-
- Send command text to a server.
-
- Entry: stream = handle to stream.
- text = handle to CR-terminated ASCII text lines.
- Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
-
- Exit: function result = system error result code.
-
- CR line terminators are translated to CRLF. A terminating "." on a line
- by itself is sent. The caller should not include this terminating line
- in the "text" block. Leading "." characters on lines are mapped to "..".
-
- If the text block does not have a terminating CR at the end of the last
- line, one is added.
- ----------------------------------------------------------------------------*/
-
- OSErr NetSendText (NetStreamHandle stream, Handle text)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
- char *p;
- long textLen;
- unsigned short len;
- char state;
-
- state = MyHGetState(text);
-
- err = MungeOut(text, true);
- if (err != noErr) goto exit;
- textLen = MyGetHandleSize(text);
-
- sh = (TStreamHandle)stream;
- s = (TStreamPtr)StripAddress(*sh);
- s->wds[1].length = 0;
- s->wds[1].ptr = nil;
- MyHLock(text);
- p = *text;
- while (textLen > 0) {
- len = textLen > 4000 ? 4000 : textLen;
- s->wds[0].length = len;
- s->wds[0].ptr = StripAddress(p);
- err = DoTCPSend(sh);
- if (err != noErr) goto exit;
- p += len;
- textLen -= len;
- }
- MyHSetState(text, state);
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- DisposeStreamBuffer(sh);
- MyHSetState(text, state);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetText
-
- Get response text from a server.
-
- Entry: stream = handle to stream.
-
- Exit: function result = system error result code.
- *text = handle to received ASCII text lines. Each line is
- terminated with a CR.
-
- CRLF line terminators are translated to CR. The server must send a
- terminating "." on a line by itself. This terminating line is not
- included in the "text" handle returned to the caller. Leading
- ".." characters on lines are mapped to ".".
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetText (NetStreamHandle stream, Handle *text)
- {
- Boolean truncated;
-
- return GetText(stream, nil, text, &truncated);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetTextWithTruncation
-
- Get response text from a server with possible truncation.
-
- Entry: stream = handle to stream.
- truncateFunction = pointer to truncate function, or nil if none.
-
- Exit: function result = system error result code.
- *text = handle to received ASCII text lines. Each line is
- terminated with a CR.
- *truncated = true if text truncated.
-
- CRLF line terminators are translated to CR. The server must send a
- terminating "." on a line by itself. This terminating line is not
- included in the "text" handle returned to the caller. Leading
- ".." characters on lines are mapped to ".".
-
- The truncation function is called every time a new block of text is
- received from the server. This function must be declared as follows:
-
- Boolean TruncateFunction (Handle t, long tLen, long *pos)
-
- Entry: t = handle to raw text received from server so far.
- tLen = length of text received from server so far.
- *pos = saved position in text (0 on first call).
-
- Exit: function result = true if text should be truncated.
- *pos = updated saved position in text if no truncation,
- length of truncated text if truncated.
-
- If the truncate function returns true, NetGetTextWithTruncation aborts
- the stream and returns to the caller immediately.
-
- The text passed to the truncate function is raw. It contains CRLF line
- terminators and doubled leading ".." characters. The text returned to
- NetGetTextWithTruncation's caller, however, is processed as indicated
- above.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetTextWithTruncation (NetStreamHandle stream, NetTruncateFunction truncateFunction,
- Handle *text, Boolean *truncated)
- {
- return GetText(stream, truncateFunction, text, truncated);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetChunkyText
-
- Get response text from a server in chunks.
-
- Entry: stream = handle to stream.
- chunkFunction = pointer to chunk processing function
-
- Exit: function result = system error result code.
- *aborted = true if the operation was aborted.
-
- The chunk processing function is called as text is received from the
- server. It must be declared as follows:
-
- OSErr ChunkFunction (Ptr t, long tLen, Boolean *abort)
-
- Entry: t = pointer to chunk.
- tLen = length of chunk.
-
- Exit: function result = error code.
- *abort = true to abort the operation.
-
- The text passed to the chunk function is raw. It contains CRLF line
- terminators and doubled leading ".." characters. The chunks do not
- necessarily end on CRLF line boundaries. The terminating "." character
- on a line by itself is *not* passed to the chunk function, however.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetChunkyText (NetStreamHandle stream, NetChunkFunction chunkFunction,
- Boolean *aborted)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
- Ptr t = nil;
- long tLen = 0, tAllocated;
- unsigned short len;
- char *tEnd;
- Boolean abort = false;
-
- sh = (TStreamHandle)stream;
- s = (TStreamPtr)StripAddress(*sh);
- tLen = s->in - s->out;
- tAllocated = tLen + 4005;
- err = MyNewPtr(tAllocated, &t);
- if (err != noErr) goto exit;
- if (tLen > 0) BlockMoveData(s->out, t, tLen);
- s->in = s->out = s->inputBuf;
-
- while (true) {
- if (tLen >= 3) {
- tEnd = t + tLen;
- if (*(tEnd-3) == '.' && *(tEnd-2) == CR && *(tEnd-1) == LF) {
- if (tLen == 3) break;
- if (tLen >= 5 && *(tEnd-5) == CR && *(tEnd-4) == LF) break;
- }
- }
- len = 4000;
- err = DoTCPRcv(sh, t + tLen, &len);
- if (err != noErr) goto exit;
- tLen += len;
- if (tLen >= 5) {
- err = (*chunkFunction)(t, tLen - 5, &abort);
- if (err != noErr) goto exit;
- if (abort) break;
- BlockMoveData(t + tLen - 5, t, 5);
- tLen = 5;
- }
- }
-
- if (abort) {
- DoTCPRelease(sh);
- DisposeStreamBuffer(sh);
- *aborted = true;
- } else {
- tLen -= 3;
- if (tLen > 0) {
- err = (*chunkFunction)(t, tLen, &abort);
- if (err != noErr) goto exit;
- }
- *aborted = false;
- }
-
- MyDisposePtr(t);
-
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- DisposeStreamBuffer(sh);
- MyDisposePtr(t);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetFTPDataOpen
-
- Open an FTP data stream.
-
- Exit: function result = system error result code.
- *port = assigned unused local port number.
- *stream = pointer to opened FTP data stream.
-
- This function returns immediately. It should be followed by a call to
- NetFTPDataWaitForConnection to wait for the FTP server to open its end of
- the data stream connection.
- ----------------------------------------------------------------------------*/
-
- OSErr NetFTPDataOpen (unsigned short *port, NetStreamHandle *stream)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
-
- err = NewStreamBuffer(&sh);
- if (err != noErr) return err;
- s = (TStreamPtr)StripAddress(*sh);
- s->in = s->out = s->inputBuf;
- s->myA5 = SetCurrentA5();
- err = DoTCPCreate(sh);
- if (err != noErr) goto exit2;
- err = DoTCPPassiveOpen(sh, port);
- if (err != noErr) goto exit1;
- *stream = (NetStreamHandle)sh;
- return noErr;
-
- exit1:
-
- DoTCPRelease(sh);
-
- exit2:
-
- MyDisposeHandle(sh);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetFTPDataClose
-
- Close an FTP data stream.
-
- Entry: stream = handle to FTP data stream.
-
- Exit: function result = system error result code (always noErr).
- ----------------------------------------------------------------------------*/
-
- OSErr NetFTPDataClose (NetStreamHandle stream)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
-
- sh = (TStreamHandle)stream;
- s = (TStreamPtr)StripAddress(*sh);
-
- /* Link the stream into the queue of closing streams. */
-
- s->finished = false;
- s->next = gCloseQueue;
- gCloseQueue = sh;
-
- /* Start an asynchronous close operation, chained to our
- completion routine. */
-
- InitTCPBlock(&s->pBlock, TCPClose);
- s->pBlock.ioCompletion =
- (TCPIOCompletionProc)gCloseStreamCompletionRoutineUPP;
- s->pBlock.tcpStream = s->tcpStream;
- err = PBControlAsync((ParmBlkPtr)&s->pBlock);
- if (err != noErr) s->finished = true;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetFTPDataWaitForConnection
-
- Wait for an FTP server to open its end of an FTP data stream.
-
- Entry: stream = handle to FTP data stream.
-
- Exit: function result = system error result code.
- ----------------------------------------------------------------------------*/
-
- OSErr NetFTPDataWaitForConnection (NetStreamHandle stream)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
-
- sh = (TStreamHandle)stream;
- s = (TStreamPtr)StripAddress(*sh);
- while (s->pBlock.ioResult > 0) {
- err = (*gGiveTime)(true);
- if (err != noErr) goto exit;
- }
- err = s->pBlock.ioResult;
- if (err != noErr) goto exit;
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- MyDisposeHandle(sh);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetFTPTextFile
-
- Get a text file from an FTP server.
-
- Entry: stream = handle to FTP data stream.
-
- Exit: function result = system error result code.
- *text = handle to received text. Each line is terminated
- with a CR.
-
- CRLF line terminators are mapped to CR.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetFTPTextFile (NetStreamHandle stream, Handle *text)
- {
- TStreamHandle sh;
- OSErr err = noErr;
- Handle t = nil;
- long tLen = 0;
- long tAllocated = 4000;
- long numFree;
- unsigned short len;
-
- sh = (TStreamHandle)stream;
- err = MyNewHandle(tAllocated, &t);
- if (err != noErr) goto exit;
-
- while (true) {
- numFree = tAllocated - tLen;
- if (numFree <= 4000) {
- tAllocated += 4000;
- err = MySetHandleSize(t, tAllocated);
- if (err != noErr) goto exit;
- }
- len = 4000;
- MyHLock(t);
- err = DoTCPRcv(sh, *t + tLen, &len);
- MyHUnlock(t);
- if (err == userCanceledErr) goto exit;
- if (err != noErr) break;
- tLen += len;
- }
-
- err = MungeIn(t, tLen, false);
- if (err != noErr) goto exit;
- *text = t;
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- MyDisposeHandle(sh);
- MyDisposeHandle(t);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetSendFTPTextFile
-
- Send a text file to an FTP server.
-
- Entry: stream = handle to FTP data stream.
- text = handle to text. Each line must be terminated by CR.
- Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
-
- Exit: function result = system error result code.
-
- CR line terminators are translated to CRLF.
- ----------------------------------------------------------------------------*/
-
- OSErr NetSendFTPTextFile (NetStreamHandle stream, Handle text)
- {
- TStreamHandle sh;
- TStreamPtr s;
- OSErr err = noErr;
- char *p;
- long textLen;
- unsigned short len;
- char state;
-
- state = MyHGetState(text);
-
- err = MungeOut(text, false);
- if (err != noErr) goto exit;
- textLen = MyGetHandleSize(text);
-
- sh = (TStreamHandle)stream;
- s = (TStreamPtr)StripAddress(*sh);
- s->wds[1].length = 0;
- s->wds[1].ptr = nil;
- MyHLock(text);
- p = *text;
- while (textLen > 0) {
- len = textLen > 4000 ? 4000 : textLen;
- s->wds[0].length = len;
- s->wds[0].ptr = StripAddress(p);
- err = DoTCPSend(sh);
- if (err != noErr) goto exit;
- p += len;
- textLen -= len;
- }
- MyHSetState(text, state);
- return noErr;
-
- exit:
-
- DoTCPRelease(sh);
- MyDisposeHandle(sh);
- MyHSetState(text, state);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetMyAddr
-
- Get this Mac's IP address.
-
- Exit: function result = system error result code.
- *addr = the IP address of this Mac.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetMyAddr (unsigned long *addr)
- {
- struct GetAddrParamBlock pBlock;
- OSErr err = noErr;
-
- memset(&pBlock, 0, sizeof(pBlock));
- pBlock.ioResult = 1;
- pBlock.csCode = ipctlGetAddr;
- pBlock.ioCRefNum = gRefNum;
- PBControlAsync((ParmBlkPtr)&pBlock);
- while (pBlock.ioResult > 0) err = (*gGiveTime)(true);
- *addr = pBlock.ourAddress;
- if (err == noErr) err = pBlock.ioResult;
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetMyAddrStr
-
- Get this Mac's IP address as a dotted-decimal string
-
- Exit: function result = system error result code.
- name = this Mac's IP address, as a C-format string.
- You must allocate at least 16 bytes for this string.
- The returned string has max length 15.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetMyAddrStr (char *addrStr)
- {
- unsigned long addr;
- OSErr err = noErr;
- static char theAddrStr[16];
- static Boolean gotIt=false;
-
- if (!gotIt) {
- err = NetGetMyAddr(&addr);
- if (err != noErr) return err;
- err = OpenResolver(nil);
- if (err != noErr) return err;
- err = AddrToStr(addr, theAddrStr);
- CloseResolver();
- if (err != noErr) return err;
- gotIt = true;
- }
- strcpy(addrStr, theAddrStr);
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetMyName
-
- Get this Mac's domain name, if any.
-
- Exit: function result = system error result code.
- name = domain name of this Mac, as a C-format string.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetMyName (CStr255 name)
- {
- unsigned long addr;
- short len;
- static OSErr err = noErr;
- static Boolean gotIt=false;
- static CStr255 theName;
-
- if (!gotIt) {
- err = NetGetMyAddr(&addr);
- if (err != noErr) return err;
- err = NetAddrToName(addr, theName);
- gotIt = true;
- len = strlen(theName);
- if (theName[len-1] == '.') theName[len-1] = 0;
- }
- if (err != noErr) return err;
- strcpy(name, theName);
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetNameToAddr
-
- Translate a domain name to an IP address.
-
- Entry: name = C-format domain name string, optionally followed by a
- comma and port number.
- defaultPort = default port number
-
- Exit: function result = system error result code.
- *addr = IP address.
- *port = port number.
- ----------------------------------------------------------------------------*/
-
- OSErr NetNameToAddr (char *name, unsigned short defaultPort,
- unsigned long *addr, unsigned short *port)
- {
- OSErr err = noErr;
- short i;
- static struct {
- CStr255 domainName;
- unsigned long addr;
- } cache[10];
- static short numCache = 0;
- struct hostInfo hInfo;
- Boolean done = false;
- Boolean canceled = false;
- CStr255 domainName;
- char *p, *q;
-
- p = name;
- q = domainName;
- while (*p !=0 && *p != ',') *q++ = *p++;
- *q = 0;
- if (*p == ',') {
- p++;
- *port = atoi(p);
- } else {
- *port = defaultPort;
- }
-
- for (i=0; i<numCache; i++) {
- if (strcmp(domainName, cache[i].domainName) == 0) {
- *addr = cache[i].addr;
- return noErr;
- }
- }
-
- err = OpenResolver(nil);
- if (err != noErr) return err;
- memset(&hInfo, 0, sizeof(hInfo));
- gDNROperationInProgress = true;
- err = StrToAddr(domainName, &hInfo, gDNRResultProcUPP, (char*)&done);
- if (err == cacheFault) {
- while (!done) canceled = (*gGiveTime)(true) == userCanceledErr || canceled;
- err = hInfo.rtnCode;
- }
- gDNROperationInProgress = false;
- (*gGiveTime)(false);
- CloseResolver();
- if (canceled) return userCanceledErr;
- if (err != noErr) return err;
- *addr = hInfo.addr[0];
-
- if (numCache < 10) {
- strcpy(cache[numCache].domainName, domainName);
- cache[numCache].addr = *addr;
- numCache++;
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetAddrToName
-
- Translate an IP address to a domain name.
-
- Entry: addr = IP address.
-
- Exit: function result = system error result code.
- name = domain name, as a C-format string.
- ----------------------------------------------------------------------------*/
-
- OSErr NetAddrToName (unsigned long addr, CStr255 name)
- {
- struct hostInfo hInfo;
- OSErr err = noErr;
- Boolean done=false;
- Boolean canceled=false;
-
- err = OpenResolver(nil);
- if (err != noErr) return err;
- memset(&hInfo, 0, sizeof(hInfo));
- gDNROperationInProgress = true;
- err = AddrToName(addr, &hInfo, gDNRResultProcUPP, (char*)&done);
- if (err == cacheFault) {
- while (!done) canceled = (*gGiveTime)(true) == userCanceledErr || canceled;
- err = hInfo.rtnCode;
- }
- gDNROperationInProgress = false;
- (*gGiveTime)(false);
- CloseResolver();
- if (canceled) return userCanceledErr;
- if (err != noErr) return err;
- hInfo.cname[254] = 0;
- strcpy(name, hInfo.cname);
- return noErr;
- }
-
-
-
-
- /*----------------------------------------------------------------------------
- NetGiveTime
-
- Give time to background processes and check for user cancel operations.
-
- Entry: waiting = true if waiting for MacTCP or something else.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGiveTime (Boolean waiting)
- {
- return (*gGiveTime)(waiting);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetDNROperationInProgress
-
- Find out whether there is a DNR operation in progress.
-
- Exit: function result = true if DNR operation in progress.
- ----------------------------------------------------------------------------*/
-
- Boolean NetDNROperationInProgress (void)
- {
- return gDNROperationInProgress;
- }
-
-
-
- /*----------------------------------------------------------------------------
- net_InitUPP
-
- Initialize UPPs.
- ----------------------------------------------------------------------------*/
-
- void net_InitUPP (void)
- {
- gDNRResultProcUPP = NewResultProc(DNRResultProc);
- gCloseStreamCompletionRoutineUPP = NewTCPIOCompletionProc(CloseStreamCompletionRoutine);
- }